﻿using CleanArchitecture.Core.Handlers;
using CleanArchitecture.Core.Http;
using CleanArchitecture.Core.Interfaces;
using CleanArchitecture.Infrastructure.Entities.Settings;
using CleanArchitecture.Infrastructure.Entities.SystemTasks;
using CleanArchitecture.Infrastructure.Interfaces;
using CleanArchitecture.Infrastructure.Interfaces.SystemLog;
using CleanArchitecture.Infrastructure.StartupConfig;
using Lucene.Net.Support;
using Microsoft.Extensions.DependencyInjection;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.Triggers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace CleanArchitecture.Infrastructure.Services.SystemTasks
{
    #region Old Implemention from nopCommerce
    /// <summary>
    /// Represents task thread
    /// </summary>
    //public partial class TaskThread : IDisposable
    //{
    //    #region Fields

    //    private static readonly int? _timeout;
    //    private static readonly string _scheduleTaskUrl;

    //    private readonly Dictionary<string, string> _tasks;
    //    private Timer _timer;
    //    private bool _disposed;

    //    #endregion

    //    #region Ctor

    //    static TaskThread()
    //    {
    //        _scheduleTaskUrl = $"{EngineContext.Current.Resolve<CommonSettings>().ServerUrl}{DefaultRoutePath.ScheduleTaskPath}";
    //        _timeout = EngineContext.Current.Resolve<CommonSettings>().ScheduleTaskRunTimeout;
    //    }

    //    internal TaskThread()
    //    {
    //        _tasks = new Dictionary<string, string>();
    //        Seconds = 10 * 60;
    //    }

    //    #endregion

    //    #region Utilities
    //    private void Run()
    //    {
    //        if (Seconds <= 0)
    //            return;

    //        StartedUtc = DateTime.UtcNow;
    //        IsRunning = true;

    //        foreach (var taskName in _tasks.Keys)
    //        {
    //            var taskType = _tasks[taskName];
    //            try
    //            {
    //                //create and configure client
    //                var client = EngineContext.Current.Resolve<IHttpClientFactory>().CreateClient(HttpDefineDefaults.DefaultHttpClient);
    //                if (_timeout.HasValue)
    //                    //DavidLee 2019/11/24 HttpClient.TimeOut Must!!! Not less or equal than 0
    //                    client.Timeout = TimeSpan.FromMilliseconds(_timeout.Value <= 60 ? 60 : _timeout.Value);

    //                //send post data
    //                var data = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>(nameof(taskType), taskType) });
    //                client.PostAsync(_scheduleTaskUrl, data).Wait();
    //            }
    //            catch (Exception ex)
    //            {
    //                var _serviceScopeFactory = EngineContext.Current.Resolve<IServiceScopeFactory>();
    //                using (var scope = _serviceScopeFactory.CreateScope())
    //                {
    //                    // Resolve
    //                    var logger = scope.ServiceProvider.GetRequiredService<ISystemLogService>();

    //                    var message = ex.InnerException?.GetType() == typeof(TaskCanceledException) ? SystemTaskMessage.ScheduleTasksTimeoutError : ex.Message;

    //                    message = string.Format(SystemTaskMessage.ScheduleTasksErrorOnRequest, taskName, message, taskType, _scheduleTaskUrl);

    //                    logger.Error(message, ex);
    //                }
    //            }
    //        }

    //        IsRunning = false;
    //    }

    //    private void TimerHandler(object state)
    //    {
    //        try
    //        {
    //            _timer.Change(-1, -1);
    //            Run();

    //            if (RunOnlyOnce)
    //                Dispose();
    //            else
    //                _timer.Change(Interval, Interval);
    //        }
    //        catch
    //        {
    //            // ignore
    //        }
    //    }

    //    #endregion

    //    #region Methods

    //    /// <summary>
    //    /// Disposes the instance
    //    /// </summary>
    //    public void Dispose()
    //    {
    //        if (_timer == null || _disposed)
    //            return;

    //        lock (this)
    //        {
    //            _timer.Dispose();
    //            _timer = null;
    //            _disposed = true;
    //        }
    //    }

    //    /// <summary>
    //    /// Inits a timer
    //    /// </summary>
    //    public void InitTimer()
    //    {
    //        if (_timer == null)
    //            _timer = new Timer(TimerHandler, null, InitInterval, Interval);
    //    }

    //    /// <summary>
    //    /// Adds a task to the thread
    //    /// </summary>
    //    /// <param name="task">The task to be added</param>
    //    public void AddTask(SystemTask task)
    //    {
    //        if (!_tasks.ContainsKey(task.TaskName))
    //            _tasks.Add(task.TaskName, task.Type);
    //    }

    //    #endregion

    //    #region Properties

    //    /// <summary>
    //    /// Gets or sets the interval in seconds at which to run the tasks
    //    /// </summary>
    //    public int Seconds { get; set; }

    //    /// <summary>
    //    /// Get or set the interval before timer first start 
    //    /// </summary>
    //    public int InitSeconds { get; set; }

    //    /// <summary>
    //    /// Get or sets a datetime when thread has been started
    //    /// </summary>
    //    public DateTime StartedUtc { get; private set; }

    //    /// <summary>
    //    /// Get or sets a value indicating whether thread is running
    //    /// </summary>
    //    public bool IsRunning { get; private set; }

    //    /// <summary>
    //    /// Gets the interval (in milliseconds) at which to run the task
    //    /// </summary>
    //    public int Interval
    //    {
    //        get
    //        {
    //            //if somebody entered more than "2147483" seconds, then an exception could be thrown (exceeds int.MaxValue)
    //            var interval = Seconds * 1000;
    //            if (interval <= 0)
    //                interval = int.MaxValue;
    //            return interval;
    //        }
    //    }

    //    /// <summary>
    //    /// Gets the due time interval (in milliseconds) at which to begin start the task
    //    /// </summary>
    //    public int InitInterval
    //    {
    //        get
    //        {
    //            //if somebody entered less than "0" seconds, then an exception could be thrown
    //            var interval = InitSeconds * 1000;
    //            if (interval <= 0)
    //                interval = 0;
    //            return interval;
    //        }
    //    }

    //    /// <summary>
    //    /// Gets or sets a value indicating whether the thread would be run only once (on application start)
    //    /// </summary>
    //    public bool RunOnlyOnce { get; set; }

    //    #endregion
    //}
    #endregion

    //// <summary>
    /// DavidLee 2020/06/22 使用Quartz.NET重新实现定时任务功能，支持Cron表达式
    /// </summary>
    [DisallowConcurrentExecution]
    public partial class TaskThread : IJob
    {
        #region Fields
        /// <summary>
        /// 一个线程任务(Job)对应一个任务记录
        /// </summary>
        private SystemTask _task;
        #endregion

        #region Ctor
        public TaskThread()
        {
        }
        #endregion

        #region override
        public Task Execute(IJobExecutionContext context)
        {

            AbstractTrigger trigger = (context as JobExecutionContextImpl).Trigger as AbstractTrigger;
            try
            {
                if (_task == null)
                {
                    var taskManager = TaskManager.Instance;
                    _task = taskManager.Tasks.Where(x => x.TaskName == trigger.Name && x.GroupName == trigger.Group).FirstOrDefault();
                }
                if (_task == null)
                    throw new TaskCanceledException(string.Format(SystemTaskMessage.ScheduleTasksErrorOnRequest, trigger.Name, trigger.Group, "未找到任务数据或可能被移除"));
                var commonSettings = EngineContext.Current.Resolve<CommonSettings>();
                if (commonSettings.ServerUrl.EndsWith("/"))
                    commonSettings.ServerUrl = commonSettings.ServerUrl.TrimEnd('/');
                string scheduleTaskUrl = $"{commonSettings.ServerUrl}{DefaultRoutePath.ScheduleTaskPath}";
                int timeout = 0; 
                if (_task.RunTimeOut.HasValue)
                    timeout = _task.RunTimeOut.Value;
                else timeout = commonSettings.ScheduleTaskRunTimeout;
                
                Console.WriteLine($"自动计划任务[{_task.TaskName}]开始:{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:sss")}");
                //create and configure client
                var client = EngineContext.Current.Resolve<IHttpClientFactory>().CreateClient(HttpDefineDefaults.DefaultHttpClient);
                //DavidLee 2019/11/24 HttpClient.TimeOut Must!!! Not less or equal than 0
                if(timeout > 0)
                    client.Timeout = TimeSpan.FromMilliseconds(timeout <= 60 ? 60 : timeout);
                //send post data
                var data = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("taskType", _task.Type) });
                client.PostAsync(scheduleTaskUrl, data).Wait();
                return null;
            }
            catch (Exception ex)
            {
                var _serviceScopeFactory = EngineContext.Current.Resolve<IServiceScopeFactory>();
                using (var scope = _serviceScopeFactory.CreateScope())
                {
                    // Resolve
                    var logger = scope.ServiceProvider.GetRequiredService<ISystemLogService>();
                    logger.Error(ex.Message);
                }
                return null;
            }
        }
        #endregion
    }
}
